package com.arcaneshift.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.arcaneshift.model.action.game.BattleEngine;
import com.arcaneshift.model.action.game.Player;
import com.common.model.action.Session;
import com.common.util.MemoryCache;
import com.common.util.SetCache;

public class GameCacheContainer {
	
	private static final String GAME_VALUES = "GameValues";
	private static final SetCache setCache;
	private static final MemoryCache<String, BattleEngine> container;
	private static final Long EXPIRE_TIME = 900000L; //15 min
	
	static {
		setCache = new SetCache(GAME_VALUES);
		container = new MemoryCache<String, BattleEngine>();
	}
	
	private GameCacheContainer() {}
	
	public static List<BattleEngine> getAll() {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		List<BattleEngine> values = new ArrayList<BattleEngine>();

		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			values.add(iterator.next().getValue());
		}
		return values;
	}
	
	public static void addGame(BattleEngine game) {
		if (game.getTime() == null) {
			game.setTime(new GregorianCalendar());
		}
		
		if (game.getId() == null) {
			//if (game.getPlayer1() != null) {
				game.setId(generateGameId());
			//}
		}
		container.put(game.getId(), game);
		setCache.add(game.getId());
	}
	
	/**
	 * Update the data inside the cache of the game.
	 * 
	 * @param game
	 */
	public static void updateGame(BattleEngine game) {
		game.setTime(new GregorianCalendar());
		container.put(game.getId(), game);
	}
	
	/**
	 * Check if the game can be saved safely.
	 * 
	 * @param id
	 * @return
	 */
	public static boolean canSave(String id, int version) {
		//FIXME Critical priority: Implement this
		return true;
	}
	
	public static BattleEngine getGame(String id) {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (game.getId().equals(id)) {
				return game;
			}
		}
		return null;
	}
	
	/**
	 * Checks if the user is in a pending or active game. 
	 * 
	 * @param userId
	 * @return true/false
	 */
	public static boolean containsId(Long userId) {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (game.containsId(userId)) {
				return true;
			}
		}
		return false;
	}
	
	public static boolean contains(String id) {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (game.getId().equals(id)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Check if the game with the given id exists in the GameContainer 
	 * and if it does, refresh it's timer.
	 * 
	 * @param id - game id
	 * @return true/false
	 */
	public static boolean containsAndRefresh(String id) {
		BattleEngine game = container.get(id);
		
		if (game != null) {
			updateGame(game);
			return true;
		} else {
			return false;
		}
	}
	
	public static void deleteGame(Long userId) {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (game.containsId(userId)) {
				container.remove(game);
				setCache.remove(game.getId());
			}
		}
	}
	
	public static void deleteExpiredGames() {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		Calendar calendar = new GregorianCalendar();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (calendar.getTimeInMillis() - game.getTime().getTimeInMillis() > EXPIRE_TIME) {
				container.remove(game);
				setCache.remove(game.getId());
			}
		}
	}
	
	public static List<Session> getAndDeleteExpiredGames() {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		List<Session> list = new ArrayList<Session>();
		Calendar calendar = new GregorianCalendar();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			if (calendar.getTimeInMillis() - game.getTime().getTimeInMillis() > EXPIRE_TIME) {
				List<Player> players = game.getPlayers();
				
				for (int i = 0; i < players.size(); i++) {
					list.add(players.get(i).getSession());
				}
				
				container.remove(game);
				setCache.remove(game.getId());
			}
		}
		return list;
	}
	
	/**
	 * Generate a random game id based on the current time.
	 * 
	 * @return
	 */
	private static String generateGameId() {
		try {
			MessageDigest messageDigest = MessageDigest.getInstance("MD5");
			StringBuilder builder = new StringBuilder();
			byte[] array = null;
			int value;
			
			messageDigest.reset();
			messageDigest.update(new Date().toString().getBytes());
			array = messageDigest.digest();
			
			for (int i = 0; i < array.length; i++) {
				value = array[i] + 0xFF;
				if (value < 16) {
					builder.append("0");
				}
				builder.append(Integer.toHexString(value));
			}
			return builder.toString();
		} catch (NoSuchAlgorithmException e) {
			return Integer.toBinaryString(new Date().toString().hashCode());
		}
	}
	
	public static void clear() {
		Map<String, BattleEngine> map = container.getAll(setCache.getValues());
		Set<Map.Entry<String, BattleEngine>> entries = map.entrySet();
		
		Iterator<Map.Entry<String, BattleEngine>> iterator = entries.iterator();
		while (iterator.hasNext()) {
			BattleEngine game = iterator.next().getValue();
			container.remove(game);
			setCache.remove(game.getId());
		}
	}
}
